package com.kaaction.modules.weixinpay;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.httpclient.NameValuePair;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.kaaction.common.utils.WechatUtils;
import com.kaaction.common.web.BaseController;
import com.kaaction.modules.mer.entity.MerOrder;
import com.kaaction.modules.mer.entity.OrderSku;
import com.kaaction.modules.mer.entity.Size;
import com.kaaction.modules.mer.service.MerOrderService;
import com.kaaction.modules.weixinpay.util.TenpayHttpClient;



/**
 * 小程序支付流程
 * @author hawk
 *
 */
@Controller
@RequestMapping(value = "/api/v1/miniprogram/pay")
public class WeixinMiniProgramApi extends BaseController {

	protected Logger logger = LoggerFactory.getLogger(getClass());
	
	@Autowired
	private MerOrderService merOrderService;

	 @RequestMapping(value = "/test")
	 public void test(HttpServletRequest request,HttpServletResponse response) throws UnsupportedEncodingException, DocumentException {
		 String basePath = request.getScheme() + "://" + request.getServerName() + ":" 
				 + request.getServerPort() + request.getContextPath();
		 String openId = request.getParameter("openId");
		 String feeStr = request.getParameter("fee");
		 double fee = 0.0;
		 fee = Double.parseDouble(feeStr);
		 String notifyUrl = basePath + "/api/v1/miniprogram/pay/notify";
		 Map<String, String> map = getParm(openId, "201705181603"+(int)(Math.random() * 1000000),"orderName", fee,notifyUrl);
		 super.writeAjaxResponse(response, map);
	 }
	 
	/**
     * 小程序支付结果页面
	 * @throws IOException 
	 * @throws DocumentException 
     * */
    @RequestMapping(value = "/notify")
    public synchronized void notify(HttpServletRequest request,HttpServletResponse response) throws IOException, DocumentException{
    	
    	BufferedReader reader = request.getReader();
		StringBuffer inputString = new StringBuffer();
		
		String line = "";
		while ((line = reader.readLine()) != null) {
			inputString.append(line);
		}
		request.getReader().close();
		logger.info("----接收到的报文---" + inputString.toString());

		Document doc = DocumentHelper.parseText(inputString.toString());
		Element root = doc.getRootElement();
		Map<String, String> map = (Map<String, String>) xml2map(root);
		
		String orderId = map.get("out_trade_no");
		String returnCode = map.get("return_code");
		String resultCode = map.get("result_code");
		String transactionId = map.get("transaction_id");
		
		logger.info("mch_id = " + map.get("mch_id"));
		logger.info("nonce_str = " + map.get("nonce_str"));
		logger.info("sign = " + map.get("sign"));
		logger.info("result_code = " + map.get("result_code"));
		logger.info("openid = " + map.get("openid"));
		logger.info("bank_type = " + map.get("bank_type"));
		logger.info("total_fee = " + map.get("total_fee"));
		logger.info("out_trade_no = " + map.get("out_trade_no"));
		logger.info("transaction_id = " + map.get("transaction_id"));
		logger.info("return_code = " + map.get("return_code"));
		logger.info("result_code = " + map.get("result_code"));
		
		String result = "SUCCESS";
		//TODO 1.验证签名
		//TODO 2.找到订单，判断该订单状态，并做逻辑处理
		if("SUCCESS".equals(returnCode)){
			MerOrder merOrder = merOrderService.get(orderId);
			if("SUCCESS".equals(resultCode)){
				if(merOrder != null && "30".equals(merOrder.getStatus())){
					merOrder.setStatus("50");
					//保存微信支付流水号
					//merOrder.set
					merOrder.setPayDate(new Date());
					merOrder.setThirdpartyCode(transactionId);
					merOrderService.save(merOrder);
				} else {
					result = "FAIL";
				}
			} else {
				if(merOrder != null && "30".equals(merOrder.getStatus())){
					merOrder.setStatus("40");
					merOrderService.save(merOrder);
				} else {
					result = "FAIL";
				}
			}
		}
		
		response.setHeader("Access-Control-Allow-Origin", "*");
		PrintWriter pw;
		try {
			pw = response.getWriter();
			pw.write("<xml><return_code>"+result+"</return_code></xml>");
			pw.flush();
			pw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
    }
	/**
	 * 提供小程序支付相关数据
	 * 
	 * @param openId
	 * @param orderId
	 * @param projectFullPath
	 *            项目具体路径，如http://cloud.kaaction.com/kaaction
	 * @return
	 * @throws UnsupportedEncodingException 
	 * @throws DocumentException 
	 */
	public Map<String, String> getParm(String openId, String orderId,String orderName, double price,String notifyFullPath) 
			throws UnsupportedEncodingException, DocumentException {

		String ip = "127.0.0.1";
		String nonce_str = UUID.randomUUID().toString().replace("-", "");
		String timestamp = String.valueOf(System.currentTimeMillis()).toString().substring(0, 10);
		String signType = "MD5";
		
		String result = this.unifiedorder(notifyFullPath, orderName, price, ip, openId, orderId);
		logger.info("unifiedorder result=" + result);
		Document doc = DocumentHelper.parseText(result);
		Element root = doc.getRootElement();
		Element prepay_id = root.element("prepay_id");
		String prepay_id_text = prepay_id.getText();
		String packageValue = "prepay_id=" + prepay_id_text;
		
		String signA = "appId=" + WechatUtils.JIEBEI_APPID + "&nonceStr=" + nonce_str + "&package=" + packageValue
					 + "&signType=" + signType + "&timeStamp=" + timestamp + "&key=" + WechatUtils.JIEBEI_SELLER_KEY;
		logger.info("to sign string =" + signA);
		String sign = DigestUtils.md5Hex(WeiXinPay.getContentBytes(signA, "UTF-8")).toUpperCase();

		Map<String, String> resultMap = new HashMap<String, String>();
		resultMap.put("package", packageValue);
		resultMap.put("timeStamp", timestamp);
		resultMap.put("nonce_str", nonce_str);
		resultMap.put("paySign", sign);
		resultMap.put("signType", signType);
		resultMap.put("orderId", orderId);
		return resultMap;
	}

	/**
	 * 统一下单接口
	 * @param notifyFullPath
	 * @param title
	 * @param price
	 * @param ip
	 * @param openId
	 * @param orderCode
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	private String unifiedorder(String notifyFullPath, String title, double price,
			String ip,String openId, String orderCode) throws UnsupportedEncodingException {

		logger.info("=========准备获取prepay_id:=========");
		
		int realpayPrice = (int) (price * 100);
		logger.info("真实支付价格为:" + realpayPrice + "分");
		
		List<NameValuePair> nvps = new ArrayList<NameValuePair>();
		// 调用统一下单接口必需传的参数,可以查看微信支付统一下单接口api查看每个参数的意思
		nvps.add(new NameValuePair("appid", WechatUtils.JIEBEI_APPID));
		nvps.add(new NameValuePair("body", title));
		nvps.add(new NameValuePair("mch_id", WechatUtils.JIEBEI_SELLER_ID));
		nvps.add(new NameValuePair("nonce_str", UUID.randomUUID().toString().replace("-", "")));
		nvps.add(new NameValuePair("notify_url", notifyFullPath)); // 回调地址需要根据实际项目做修改
		nvps.add(new NameValuePair("openid", openId));
		nvps.add(new NameValuePair("out_trade_no", orderCode));
		nvps.add(new NameValuePair("spbill_create_ip", ip));// ip地址需要根据实际项目做修改
		nvps.add(new NameValuePair("total_fee", realpayPrice + ""));
		nvps.add(new NameValuePair("trade_type", "JSAPI"));

		logger.info("真实支付价格为:" + realpayPrice + "分");
		logger.info("支付回调地址为" + notifyFullPath);
		StringBuffer sb = new StringBuffer();

		for (NameValuePair nvp : nvps) {
			sb.append(nvp.getName() + "=" + nvp.getValue() + "&");
		}

		String signA = sb.toString(); // 根据签名格式组装数据，详见微信支付api
		String stringSignTemp = signA + "key=" + WechatUtils.JIEBEI_SELLER_KEY; // 根据签名格式组装数据，详见微信支付api
		String sign = DigestUtils.md5Hex(WeiXinPay.getContentBytes(stringSignTemp, "UTF-8")).toUpperCase();// 把组装好的签名数据md5之后字母都转换为大写，详见微信支付api
		nvps.add(new NameValuePair("sign", sign)); // 把签名后的数据组装成参数
		TenpayHttpClient httpClient = new TenpayHttpClient();
		httpClient.setReqContent(WechatUtils.PREPAY_URL);
		String resContent = "";
		String result = "";
		if (httpClient.callHttpPost(WechatUtils.PREPAY_URL, toXml(nvps))) {
			resContent = httpClient.getResContent();
			result = new String(resContent.getBytes("UTF-8"));
		}
		return result;
	}

	
//	public static void main(String[] args) throws UnsupportedEncodingException {
//		String signA = "appId=wxd678efh567hg6787&nonceStr=5K8264ILTKCH16CQ2502SI8ZNMTM67VS&package=prepay_id=wx2017033010242291fcfe0db70013231072"
//				+ "&signType=MD5&timeStamp=1490840662&key=qazwsxedcrfvtgbyhnujmikolp111111";
//		String stringSignTemp = signA; // 根据签名格式组装数据，详见微信支付api
//		String sign = DigestUtils.md5Hex(WeiXinPay.getContentBytes(stringSignTemp, "UTF-8")).toUpperCase();
//		System.out.println(sign);
//	}

	public static String loadJson(String url) {
		StringBuilder json = new StringBuilder();
		try {
			URL urlObject = new URL(url);
			URLConnection uc = urlObject.openConnection();
			BufferedReader in = new BufferedReader(new InputStreamReader(uc.getInputStream()));
			String inputLine = null;
			while ((inputLine = in.readLine()) != null) {
				json.append(inputLine);
			}
			in.close();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return json.toString();
	}

	private Map xml2map(Element e) {
		Map map = new LinkedHashMap();
		List list = e.elements();
		if (list.size() > 0) {
			for (int i = 0; i < list.size(); i++) {
				Element iter = (Element) list.get(i);
				List mapList = new ArrayList();

				if (iter.elements().size() > 0) {
					Map m = xml2map(iter);
					if (map.get(iter.getName()) != null) {
						Object obj = map.get(iter.getName());
						if (!(obj instanceof List)) {
							mapList = new ArrayList();
							mapList.add(obj);
							mapList.add(m);
						}
						if (obj instanceof List) {
							mapList = (List) obj;
							mapList.add(m);
						}
						map.put(iter.getName(), mapList);
					} else
						map.put(iter.getName(), m);
				} else {
					if (map.get(iter.getName()) != null) {
						Object obj = map.get(iter.getName());
						if (!(obj instanceof List)) {
							mapList = new ArrayList();
							mapList.add(obj);
							mapList.add(iter.getText());
						}
						if (obj instanceof List) {
							mapList = (List) obj;
							mapList.add(iter.getText());
						}
						map.put(iter.getName(), mapList);
					} else
						map.put(iter.getName(), iter.getText());
				}
			}
		} else
			map.put(e.getName(), e.getText());
		return map;
	}
	
	// 转换成xml格式
		private String toXml(List<NameValuePair> params) {
			StringBuilder sb = new StringBuilder();
			sb.append("<xml>");
			for (int i = 0; i < params.size(); i++) {
				sb.append("<" + params.get(i).getName() + ">");
				sb.append("<![CDATA[");
				sb.append((params.get(i)).getValue());
				sb.append("]]>");
				sb.append("</" + params.get(i).getName() + ">");
			}
			sb.append("</xml>");
			System.out.println("xml=" + sb.toString());
			String ss = null;
			try {
				ss = new String(sb.toString().getBytes("UTF-8"));
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
			return ss;
		}

		// 编码转换
		public static byte[] getContentBytes(String content, String charset) {
			if (charset == null || "".equals(charset)) {
				return content.getBytes();
			}
			try {
				return content.getBytes(charset);
			} catch (UnsupportedEncodingException e) {
				throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
			}
		}

		/**
		 * 把中文转成Unicode码
		 * 
		 * @param str
		 * @return
		 */
		public String chinaToUnicode(String str) {
			String result = "";
			for (int i = 0; i < str.length(); i++) {
				int chr1 = (char) str.charAt(i);
				if (chr1 >= 19968 && chr1 <= 171941) {// 汉字范围 \u4e00-\u9fa5 (中文)
					result += "\\u" + Integer.toHexString(chr1);
				} else {
					result += str.charAt(i);
				}
			}
			return result;
		}


}
